
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package zeroticket;

import com.google.common.primitives.Bytes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.smartcardio.ATR;
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import javax.swing.JOptionPane;

/**
 *
 * @author giovannirojas
 */
public class DPIReader {

    private DPIinfo dpiReaded = null;
    private CardTerminal cardTerminal = null;
    byte[] photoBytes = null; // Los bytes de la foto 

    public DPIReader() {

        dpiReaded = null;
        cardTerminal = TerminalCardEMV.getCardTerminalSystem();
    }

    public void doCardReaderCommunication(CardTerminal terminal) throws DPIParseException {

        if (terminal != null) {
            cardTerminal = terminal;
            while (true) {

                try {
                    cardTerminal.waitForCardPresent(1000);
                    System.out.println("Inserted card");
                    handleCard(cardTerminal);
                    cardTerminal.waitForCardAbsent(1000);
                    System.out.println("Removed card");

                    break;
                } catch (CardException ex) {
                    Logger.getLogger(DPIReader.class.getName()).log(Level.SEVERE, null, ex);
                }

            }
        } else {
            JOptionPane.showMessageDialog(null, "Error, terminal es nulo ","Error en lectura de DPI",0);
        }

    }

    private void handleCard(CardTerminal cardTerminal) throws DPIParseException {

        Card card;
        CardChannel channel;
        ResponseAPDU response;
        String[] dataDpi = null;

        try {
            card = cardTerminal.connect("*");
            ATR atr = card.getATR();
            card.beginExclusive();
            if (knownATR(atr)) {  // Si cumple con los ATR's
                try {
                    
                    channel = card.getBasicChannel();

                    // "1PAY.SYS.DDF01"    
                    // Solo de referencia para mantener el formato de las tarjetas dpi, 
                    // lo que se esta mandando es cl_cla, cl_ins_get_uid, p1, p2, datos de referencia, tamanio de vuelta
                    response = channel.transmit(new CommandAPDU(0, 164, 4, 12, firstLine, 0));

                    if (response.getSW1() == 144) {  // Quiere decir que la respuesta es correcta 144 es 90 en hexadecimal

                        response = channel.transmit(new CommandAPDU(0, 164, 0, 0, secondLine, 0));

                        if (response.getSW1() == 144) {  // Quiere decir que la respuesta es correcta 144 es 90 en hexadecimal

                            response = channel.transmit(new CommandAPDU(0, 164, 0, 0, thirdLine, 0));

                            if (response.getSW1() == 144) {  // Quiere decir que la respuesta es correcta 144 es 90 en hexadecimal

                                response = channel.transmit(new CommandAPDU(0, 164, 2, 0, forthLine, 0));

                                if (response.getSW1() == 144) {

                                    response = channel.transmit(new CommandAPDU(fifthLine));

                                    if (response.getSW1() == 144) {

                                        // En este momento es donde se empiezan a ver datos por lo que los convertire. 
                                        String data = "";

                                        for (int i = 0; i < response.getData().length; i++) {
                                            data += (char) (response.getData()[i]);

                                        }

                                        // Creo mi variable de lista 
                                        ArrayList<String> dataList = new ArrayList<String>();
                                        // Divido Los datos, lo hago de esta manera por orden. 

                                        String dpi = data.substring(0, 13);
                                        dataList.add(dpi);
                                        String primerNombre = data.substring(13, 38);
                                        dataList.add(primerNombre);
                                        String segundoNombre = data.substring(38, 46);
                                        dataList.add(segundoNombre);
                                        String tercerNombre = data.substring(46, 63);
                                        dataList.add(tercerNombre);
                                        String primerApellido = data.substring(63, 88);
                                        dataList.add(primerApellido);
                                        String segundoApellido = data.substring(88, 138);
                                        dataList.add(segundoApellido);
                                        String sexo = data.substring(138, 147);
                                        dataList.add(sexo);
                                        String vecindadMunicipio = data.substring(147, 177);
                                        dataList.add(vecindadMunicipio);
                                        String vecindadDepartamento = data.substring(177, 207);
                                        dataList.add(vecindadDepartamento);
                                        String nacionalidad = data.substring(207, 237);
                                        dataList.add(nacionalidad);
                                        String nacimiento = data.substring(237, 247);
                                        dataList.add(nacimiento);
                                        String emisionFecha = data.substring(247, 256);
                                        dataList.add(emisionFecha);

                                        //Transformo de lista a array de String 
                                        Object[] objectList = dataList.toArray();
                                        dataDpi = Arrays.copyOf(objectList, objectList.length, String[].class);

                                        response = channel.transmit(new CommandAPDU(sixthLine));

                                        if (response.getSW1() == 144) {
                                            response = channel.transmit(new CommandAPDU(0, 164, 2, 0, seventhLine, 0));

                                            if (response.getSW1() == 144) {
                                                response = channel.transmit(new CommandAPDU(eightLine));

                                                if (response.getSW1() == 144) {
                                                    // Linea 8 hay algo interesante 
                                                    data = "";

                                                    for (int i = 0; i < response.getData().length; i++) {
                                                        data += (char) (response.getData()[i]);

                                                    }

                                                    System.out.println(data);
                                                    response = channel.transmit(new CommandAPDU(nineLine));

                                                    if (response.getSW1() == 144) {
                                                        response = channel.transmit(new CommandAPDU(tenLine));

                                                        if (response.getSW1() == 144) {

                                                            // Leo la foto 
                                                            readDPIPhoto(response, channel); // de aqui en adelante es informacion de la foto. 

                                                            dpiReaded = new DPIinfo(dataDpi, photoBytes); // Guardo la informacion 

                                                            card.endExclusive();

                                                            try {

                                                                card.disconnect(false);

                                                            } catch (CardException ex) {
                                                                try {
                                                                    dpiReaded = null; // Es nulo si da error
                                                                    throw new DPIParseException("Error al desconectar DPI : " + ex.getMessage());
                                                                } catch (DPIParseException ex1) {
                                                                    // Ya lo maneje arriba 
                                                                }

                                                            }

                                                            System.out.println("End read. ! ");

                                                        }
                                                    }

                                                }
                                            }
                                        }
                                    }

                                } // Fin de la cuarta 
                            } // Fin tercera
                        } // Fin segunda
                    } // Fin primera 

                } catch (CardException e) {
                    dpiReaded = null;
                    throw new DPIParseException("Un error ocurió al leer DPI " + e);

                }
            } else {
                // No es un DPI, pero aun asi debo desconectar el DPI 

                card.endExclusive();

                try {

                    card.disconnect(false);

                } catch (CardException ex) {
                    try {
                        dpiReaded = null; // Es nulo si da error
                        throw new DPIParseException("Error al desconectar DPI : " + ex.getMessage());
                    } catch (DPIParseException ex1) {
                        // Ya lo maneje arriba 
                    }

                }

                System.out.println("End read. ! ");

                dpiReaded = null;
                throw new DPIParseException("Un error ocurió al leer el identificador del DPI (ATR) ");

            }
        } catch (CardException e) {
            dpiReaded = null;
            throw new DPIParseException("Error al leer DPI, aparentemente DPI no presente, volverlo a intentar. " + e);

            /*try {
             Thread.sleep(100);
             } catch (InterruptedException ex) {
             ex.printStackTrace();
             }*/ // Lo creo inecesario, solo es para esperar a que cambie de tarjeta 
        }

    }

    /*
     Getters 
     */
    public DPIinfo getDPI() {

        return dpiReaded;
    }

    /*
     Metodo para leer la foto del DPI 
     */
    private void readDPIPhoto(ResponseAPDU response, CardChannel channel) throws DPIParseException {
        try {
            // Variables que utilizo para leer el DPI
            byte[] photoAPDUArray = null;
            String[] dataDpi = null;
            String dataPrueba = null;

            ArrayList<Byte> fotoBytes = new ArrayList<Byte>();

            // Onceava Linea   
            photoAPDUArray = new byte[]{
                (byte) 0x00,
                (byte) 0xB0,
                (byte) 0x82,
                (byte) 0x00,
                (byte) 0x00

            };
            response = channel.transmit(new CommandAPDU(photoAPDUArray));

            /* En formato de JPG la imagen debe de iniciar con el byte 0xFF luego de 0xD8 */
            if (response.getSW1() == 144) {

                dataPrueba = "";
                for (int i = 104; i < response.getData().length; i++) {
                    dataPrueba += (char) (response.getData()[i]);
                    fotoBytes.add(response.getData()[i]);

                }

                // Aqui corresponde los bits de la foto. 
                for (int b = 1; b < 90; b++) {

                    photoAPDUArray = new byte[]{
                        (byte) 0x00,
                        (byte) 0xB0,
                        (byte) b,
                        (byte) 0x00,
                        (byte) 0x00

                    };
                    response = channel.transmit(new CommandAPDU(photoAPDUArray));

                    if (response.getSW1() == 144) {
                        dataPrueba = "";
                        for (int i = 0; i < response.getData().length; i++) {
                            dataPrueba += (char) (response.getData()[i]);
                            fotoBytes.add(response.getData()[i]);

                        }

                    }
                }

            } // Fin de la 11 linea 

            photoBytes = Bytes.toArray(fotoBytes);

        } catch (CardException ex) {
            // Esto es para que de un error. 
            dpiReaded = null;
            throw new DPIParseException("Un error ocurió al leer foto del DPI " + ex);

        }
    }

    /*
     * reciviendo como parametro el atr de la tarjeta de crédito que esta metiendo. 
     */
    private boolean knownATR(ATR atr) {

        // Aqui verifica si es visa del Banco Industrial 
        if (Arrays.equals(know_dpi_tars, atr.getBytes())) {
            return true;
        }

        return false;
    }

    /* 
     * Esto es para todos los DPI, deben de tener este formato de ATR, que es la manera de respueseta. 
     */
    private static final byte[] know_dpi_tars
            = {(byte) 0x3B, (byte) 0xDB, (byte) 0x96, (byte) 0x00, (byte) 0x80, (byte) 0xB1, (byte) 0xFE, (byte) 0x45, (byte) 0x1F, (byte) 0x83, (byte) 0x00, (byte) 0x31, (byte) 0xC0, (byte) 0x64, (byte) 0xC3, (byte) 0x08, (byte) 0x01, (byte) 0x00, (byte) 0x0F, (byte) 0x90, (byte) 0x00, (byte) 0x9B};

    // Lineas para obtener la informacion del DPI 
    private static final byte[] firstLine = new byte[]{
        (byte) 160,
        (byte) 0,
        (byte) 0,
        (byte) 0,
        (byte) 119,
        (byte) 1,
        (byte) 131,
        (byte) 131,
        (byte) 8,
        (byte) 16,
        (byte) 0,
        (byte) 241,
        (byte) 0,
        (byte) 0,
        (byte) 0,
        (byte) 1

    };

    private static final byte[] secondLine = new byte[]{
        (byte) 63,
        (byte) 0

    };

    private static final byte[] thirdLine = new byte[]{
        (byte) 16,
        (byte) 1

    };

    private static final byte[] forthLine = new byte[]{
        (byte) 1,
        (byte) 1

    };

    private static final byte[] fifthLine = new byte[]{
        (byte) 0,
        (byte) 176,
        (byte) 0,
        (byte) 0,
        (byte) 0

    };

    private static final byte[] sixthLine = new byte[]{
        (byte) 0,
        (byte) 176,
        (byte) 0,
        (byte) 255,
        (byte) 0

    };

    private static final byte[] seventhLine = new byte[]{
        (byte) 1,
        (byte) 2

    };

    private static final byte[] eightLine = new byte[]{
        (byte) 0,
        (byte) 176,
        (byte) 0,
        (byte) 0,
        (byte) 0

    };

    private static final byte[] nineLine = new byte[]{
        (byte) 0,
        (byte) 0xB0,
        (byte) 0x00,
        (byte) 0XFF,
        (byte) 0

    };

    private static final byte[] tenLine = new byte[]{
        (byte) 0x00,
        (byte) 0xA4,
        (byte) 0x04,
        (byte) 0x0C,
        (byte) 0x07,
        (byte) 0xA0,
        (byte) 0x00,
        (byte) 0x00,
        (byte) 0x02,
        (byte) 0x47,
        (byte) 0x10,
        (byte) 0x01};

    public CardTerminal getCardTerminal() {
        return cardTerminal;
    }

    public void setCardTerminal(CardTerminal cardTerminal) {
        this.cardTerminal = cardTerminal;
    }

    
}
